package org.computate.model.item;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.Id;
import javax.persistence.PostLoad;
import javax.persistence.Transient;

import org.computate.model.dao.Daos;
import org.computate.model.dao.PhotoDao;
import org.computate.model.item.field.ItemField;
import org.computate.tool.DateTool;

import com.google.inject.Inject;
import com.googlecode.objectify.annotation.Cached;
import com.googlecode.objectify.annotation.Entity;

@Cached
@Entity
public class Model implements Comparable<Model> {
	
	public String getModelDisplayName() { return "Model"; }
	public String getModelPluralName() { return "Models"; }
	public Integer getGetLevels() { return 2; }
	public Integer getListLevels() { return 2; }
	public boolean getBackButton() { return true; }
	public String getAddLink() { return null; }
	public String getAddLinkName() { return null; }
	public boolean getNameInTitle() { return true; }
	@Id Long id;
	String name;
	Date created;
	Date modified;
	String modelName;
	String displayName;
	String description;
	@Transient boolean open = false;
	@Transient boolean photoPermitted = false;
	@Transient boolean photosPermitted = false;
	@Transient String order = null;
	@Transient Photo photo = null;
	@Transient List<Photo> photos = new ArrayList<Photo>();
	Boolean approved = true;
	@Transient List<ItemField> fields = new ArrayList<ItemField>();
	private Integer indexColumns = null;
	@Inject PhotoDao photoDao;
	@Transient private boolean populated = false;
	
	public Model() {
		setCreated(new Date());
		setModelName(this.getClass().getSimpleName());
	}
	
	@PostLoad void onLoad() {
	}
	
	@Override
	public int compareTo(Model o) {
		if(o == null)
			return -1;
		
		String name2 = o.getName();
		
		if(name == null)
			return 1;
		if(name2 == null)
			return -1;
		return name.compareTo(name2);
	}
	
//	public void populateValues(HttpServletRequest r) {
//		if(r != null) {
//  			for(FormItem itemObject : items) {
//				populateValues(r, itemObject);
//			}
//		}
//	}
	
	public ItemField getField(String fieldName) {
		ItemField result = null;
		
		if(fieldName != null && fields != null) {
			ItemField key = new ItemField();
			int i;
			
			key.setName(fieldName);
			i = fields.indexOf(key);
			if(i >= 0) {
				result = fields.get(i);
			}
		}
		
		return result;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public Long getId() {
		return id;
	}

	public void setName(String name) {
		this.name = name;
		setDisplayName(name);
	}

	public String getName() {
		return name;
	}

	public void setCreated(Date created) {
		this.created = created;
	}

	public Date getCreated() {
		return created;
	}

	public void setModified(Date modified) {
		this.modified = modified;
	}

	public Date getModified() {
		return modified;
	}
	
	public void add(Model model) {
		
	}
	
	public void remove(Model model) {
		
	}
	
	public boolean isOrdered() {
		return false;
	}
	
	public String getModelName() {
		return modelName;
	}

	public void setModelName(String modelName) {
		this.modelName = modelName;
	}

	public String getDisplayName() {
		return displayName;
	}

	public void setDisplayName(String displayName) {
		this.displayName = displayName;
	}

	public String getOrder() {
		return order;
	}

	public void setOrder(String order) {
		this.order = order;
	}

	public boolean isOpen() {
		return open;
	}

	public void setOpen(boolean open) {
		this.open = open;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}
	
	public Integer getDaysOld() {
		return DateTool.getDaysOld(getCreated());
	}

	public boolean isPhotoPermitted() {
		return photoPermitted;
	}

	public void setPhotoPermitted(boolean photoPermitted) {
		this.photoPermitted = photoPermitted;
	}

	public boolean isPhotosPermitted() {
		return photosPermitted;
	}

	public void setPhotosPermitted(boolean photosPermitted) {
		this.photosPermitted = photosPermitted;
	}

	public Photo getPhoto() {
		return photo;
	}

	public void setPhoto(Photo photo) {
		this.photo = photo;
	}

	public List<Photo> getPhotos() {
		return photos;
	}

	public void setPhotos(List<Photo> photos) {
		this.photos = photos;
	}

	public Boolean getApproved() {
		return approved;
	}

	public void setApproved(Boolean approved) {
		this.approved = approved;
	}
	
	public List<ItemField> getFields() {
		return fields;
	}
	
	protected ItemField addField(String name, String label) {
		ItemField item = createField(name, label);
		
		if(!this.fields.contains(item))
			this.fields.remove(item);
		this.fields.add(item);
		
		return item;
	}
	
	protected ItemField addField(String name) {
		ItemField item = createField(name, null);
		
		this.fields.add(item);
		
		return item;
	}
	
	protected ItemField removeField(String name) {
		ItemField item = createField(name, null);
		
		this.fields.remove(item);
		
		return item;
	}
	
	protected ItemField createField(String name, String label) {
		ItemField itemField = new ItemField();
		
		try {
			Method getter = new PropertyDescriptor(name, this.getClass()).getReadMethod();
			Class<?> c = getter.getReturnType();
			itemField.setC(c);
			
			if(List.class.isAssignableFrom(c)) {
		        ParameterizedType genericType = (ParameterizedType)getter.getGenericReturnType();
		        Class<?> genericClass = (Class<?>)genericType.getActualTypeArguments()[0];
		        
				try {
					if(Model.class.isAssignableFrom(genericClass)) {
						String modelName = genericClass.getSimpleName();
						Model newInstance = (Model)genericClass.newInstance();
						
						if(modelName != null) {
							itemField.setItemName(modelName);
							itemField.setItemDisplayName(newInstance.getModelDisplayName());
							itemField.setItemPluralName(newInstance.getModelPluralName());
						}
					}
				} catch (InstantiationException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			else if(Model.class.isAssignableFrom(c)) {
				try {
					String modelName = c.getSimpleName();
					Model newInstance = (Model)c.newInstance();
					
					if(modelName != null) {
						itemField.setItemName(modelName);
						itemField.setItemDisplayName(newInstance.getModelDisplayName());
						itemField.setItemPluralName(newInstance.getModelPluralName());
					}
				} catch (InstantiationException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IntrospectionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		itemField.setName(name);
		itemField.setLabel(label);
		
		return itemField;
	}
	public Integer getIndexColumns() {
		return indexColumns;
	}
	public void setIndexColumns(Integer indexColumns) {
		this.indexColumns = indexColumns;
	}
	
	public void addFields() {
		fields.clear();
		addField("name", "Name:").setInIndex(true).setHeader(2);
	}
	
	public void addItems(int levels, Daos daos) {
		String className = this.getClass().getSimpleName();
		
		List<Photo> photos = daos.getPhotoDao().ofy().query(Photo.class).filter("itemName", className).filter("itemId", id).list();
		if(photosPermitted)
			this.photos = photos;
		if((photosPermitted || photoPermitted) && photos.size() > 0)
			photo = photos.get(0);
	}
	public PhotoDao getPhotoDao() {
		return photoDao;
	}
	public void setPhotoDao(PhotoDao photoDao) {
		this.photoDao = photoDao;
	}
	public boolean isPopulated() {
		return populated;
	}
	public void setPopulated(boolean populated) {
		this.populated = populated;
	}
}
